<?php
namespace App\Pay\Gateway\Alipay;

use App\Pay\Exceptions\InvalidArgumentException;

class Sign
{
    public static function get(array $params): string
    {
        $privateKey = Request::instance()->config()->get('private_key', null);
        if (is_null($privateKey)) {
            throw new InvalidArgumentException(' private key is empty');
        }
        $privateKey = "-----BEGIN RSA PRIVATE KEY-----\n".
            wordwrap($privateKey, 64, "\n", true).
            "\n-----END RSA PRIVATE KEY-----";

        openssl_sign(self::getSignContent($params), $sign, $privateKey, OPENSSL_ALGO_SHA256);

        $sign = base64_encode($sign);
        if (is_resource($privateKey)) {
            openssl_free_key($privateKey);
        }

        return $sign;
    }

    public static function checkSign(array $data, $sync = false, $sign = null)
    {
        $publicKey = Request::instance()->config()->get('public_key', null);
        if (is_null($publicKey)) {
            throw new InvalidArgumentException(' public key is empty');
        }
        $publicKey = "-----BEGIN PUBLIC KEY-----\n".
            wordwrap($publicKey, 64, "\n", true).
            "\n-----END PUBLIC KEY-----";
        $sign = $sign ?? $data['sign'];
        $toVerify = $sync ? json_encode($data, JSON_UNESCAPED_UNICODE) : self::getSignContent($data, true);
        $isVerify = 1 === openssl_verify($toVerify, base64_decode($sign), $publicKey, OPENSSL_ALGO_SHA256);
        if (is_resource($publicKey)) {
            openssl_free_key($publicKey);
        }
        return $isVerify;
    }

    public static function getSignContent(array $data, $verify = false): string
    {
        ksort($data);
        $stringToBeSigned = '';
        foreach ($data as $k => $v) {
            if ($verify && 'sign' != $k && 'sign_type' != $k) {
                $stringToBeSigned .= $k.'='.$v.'&';
            }
            if (!$verify && '' !== $v && !is_null($v) && 'sign' != $k && '@' != substr($v, 0, 1)) {
                $stringToBeSigned .= $k.'='.$v.'&';
            }
        }
        return trim($stringToBeSigned, '&');
    }
}
